package br.edu.ufcg.lsd.wbis.control.user;

import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Arrays;
import java.util.LinkedList;

import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Test;

import br.edu.ufcg.lsd.wbis.control.exception.DuplicatedIdentifierException;
import br.edu.ufcg.lsd.wbis.control.exception.UnknownEntityException;
import br.edu.ufcg.lsd.wbis.control.util.SecurityUtil;

public class UserManagerTest {

	private final UserDAO daoMock = EasyMock.createMock(UserDAO.class);
	private UserManager manager;

	@After
	public void tearDown() throws Exception {
		EasyMock.reset(daoMock);
		manager = null;
	}

	@Test
	public void testInitializeWithNoUsers() {
		reset(daoMock);
		expect(daoMock.getUsers()).andReturn(new LinkedList<User>());
		replay(daoMock);
		manager = new UserManager(daoMock);
		assertTrue(manager.getUsers().isEmpty());
	}

	@Test(expected = UnknownEntityException.class)
	public void testInitializeWithSomeUsers() throws Exception {
		User john = new User("John", "john@dodman.com.au", "john", "j123", false, UserLevel.COMMON_USER);
		User peter = new User("Peter Mescal", "mescal@aurevoir.fr", "petre", "ppp", false, UserLevel.COMMON_USER);
		User adele = new User("Adele Martins", "adelemar@usa.com", "adel", "johan", true, UserLevel.ADMIN);

		reset(daoMock);
		expect(daoMock.getUsers()).andReturn(Arrays.asList(john, peter, adele));
		replay(daoMock);

		manager = new UserManager(daoMock);
		try {
			assertEquals(peter, manager.getUser("petre"));
			assertEquals(john, manager.getUser("john"));
			assertEquals(adele, manager.getUser("adel"));
		} catch (UnknownEntityException e) {
			fail("shouldn't throw exception");
		}

		try {
			manager.getUser("unknownUser");
		} finally {
			verify(daoMock);
		}
	}

	@Test(expected = DuplicatedIdentifierException.class)
	public void testAddUser() throws Exception {
		reset(daoMock);
		expect(daoMock.getUsers()).andReturn(new LinkedList<User>());
		replay(daoMock);

		manager = new UserManager(daoMock);

		User john = new User("John", "john@dodman.com.au", "john", SecurityUtil.generateHash("j123"), false, UserLevel.COMMON_USER);
		reset(daoMock);
		daoMock.addUser(john);
		replay(daoMock);

		try {
			manager.addUser("john", "j123", "John", "john@dodman.com.au", false, UserLevel.COMMON_USER);
			assertEquals(john, manager.getUser("john"));
		} catch (DuplicatedIdentifierException e) {
			fail("shouldn't throw exception");
		}

		verify(daoMock);

		User adele = new User("Adele Martins", "adelemar@usa.com", "adel", SecurityUtil.generateHash("johan"), true, UserLevel.ADMIN);
		reset(daoMock);
		daoMock.addUser(adele);
		replay(daoMock);

		try {
			manager.addUser("adel", "johan", "Adele Martins", "adelemar@usa.com", true, UserLevel.ADMIN);

			assertEquals(adele, manager.getUser("adel"));
		} catch (DuplicatedIdentifierException e) {
			fail("shouldn't throw exception");
		}

		verify(daoMock);

		try {
			manager.addUser("john", "obc", "John Hatway", "john@bigpound.com", false, UserLevel.COMMON_USER);
		} finally {
			verify(daoMock);
		}
	}

	@Test(expected = UnknownEntityException.class)
	public void testRemoveUser() throws UnknownEntityException {
		User john = new User("John", "john@dodman.com.au", "john", "j123", false, UserLevel.COMMON_USER);
		User peter = new User("Peter Mescal", "mescal@aurevoir.fr", "petre", "ppp", false, UserLevel.COMMON_USER);
		User adele = new User("Adele Martins", "adelemar@usa.com", "adel", "johan", true, UserLevel.ADMIN);

		reset(daoMock);
		expect(daoMock.getUsers()).andReturn(Arrays.asList(john, peter, adele));
		replay(daoMock);
		manager = new UserManager(daoMock);

		reset(daoMock);
		daoMock.removeUser(eq(peter));
		replay(daoMock);

		try {
			manager.removeUser("petre");
		} catch (UnknownEntityException e) {
			fail("should not throw exception");
		}

		verify(daoMock);

		try {
			manager.getUser("petre");
			fail("should have thrown exception");
		} catch (UnknownEntityException e) {
		}

		reset(daoMock);
		daoMock.removeUser(eq(adele));
		replay(daoMock);

		try {
			manager.removeUser("adel");
		} catch (UnknownEntityException e) {
			fail("should not throw exception");
		}

		verify(daoMock);
		reset(daoMock);
		replay(daoMock);

		try {
			manager.removeUser("jeff");
		} finally {
			verify(daoMock);
		}
	}

	@Test
	public void testUpdateUser() throws UnknownEntityException {
		User john = new User("John", "john@dodman.com.au", "john", "j123", false, UserLevel.COMMON_USER);
		User peter = new User("Peter Mescal", "mescal@aurevoir.fr", "petre", "ppp", false, UserLevel.COMMON_USER);
		User adele = new User("Adele Martins", "adelemar@usa.com", "adel", "johan", true, UserLevel.ADMIN);

		reset(daoMock);
		expect(daoMock.getUsers()).andReturn(Arrays.asList(john, peter, adele));
		replay(daoMock);
		manager = new UserManager(daoMock);

		peter.setEmail("mescalp@cnet.com");
		peter.setLevel(UserLevel.ADMIN);

		reset(daoMock);
		daoMock.updateUser(eq(peter));
		replay(daoMock);

		manager.updateUserInfo("petre", "j123", "mescalp@cnet.com", UserLevel.ADMIN, true);
		assertEquals(peter, manager.getUser("petre"));

		verify(daoMock);
		reset(daoMock);
		replay(daoMock);

		try {
			manager.updateUserInfo("jonh", "jonh123", "john@cade.com", UserLevel.COMMON_USER, false);
			fail("should have thrown exception");
		} catch (UnknownEntityException e) {
		}

		verify(daoMock);

		adele.setName("Adel");
		adele.setPassword("adelia");
		adele.setEmail("adel@micro.com");
		adele.setLevel(UserLevel.COMMON_USER);

		reset(daoMock);
		daoMock.updateUser(eq(adele));
		replay(daoMock);

		manager.updateUserInfo("adel", "adelia", "adel@micro.com", UserLevel.COMMON_USER, false);
		assertEquals(adele, manager.getUser("adel"));

		verify(daoMock);
	}
}
